/**
 * create by zhike date:2015年3月12日
 */
package com.acooly.module.openapi.client.provider.fbank.marshall;

import com.acooly.core.utils.Money;
import com.acooly.core.utils.Reflections;
import com.acooly.core.utils.Strings;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.marshal.ApiUnmarshal;
import com.acooly.module.openapi.client.api.message.MessageFactory;
import com.acooly.module.openapi.client.api.util.ApiTypes;
import com.acooly.module.openapi.client.api.util.MoneyDeserializer;
import com.acooly.module.openapi.client.provider.fbank.FbankConstants;
import com.acooly.module.openapi.client.provider.fbank.domain.FbankApiMsgInfo;
import com.acooly.module.openapi.client.provider.fbank.domain.FbankResponse;
import com.acooly.module.openapi.client.provider.fbank.enums.FbankRespCodeEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/**
 * @author zhike
 */
@Service
@Slf4j
public class FbankResponseUnmarshall extends FbankMarshallSupport
        implements ApiUnmarshal<FbankResponse, Map<String, String>> {

    @Resource(name = "fbankMessageFactory")
    private MessageFactory messageFactory;

    @SuppressWarnings("unchecked")
    @Override
    public FbankResponse unmarshal(Map<String, String> respMessage, String serviceName) {
        try {
            String message = respMessage.get(FbankConstants.RESP_MESSAGE_KEY);
            log.info("响应报文:{}", message);
            FbankResponse response = doUnmarshall(message, serviceName);
            //验签
            checkVerySign(response, respMessage);
            log.info("验签成功");
            return response;
        } catch (Exception e) {
            throw new ApiClientException("解析响应报文错误:" + e.getMessage());
        }
    }

    protected FbankResponse doUnmarshall(String message, String serviceName) {
        FbankResponse response = (FbankResponse) messageFactory.getResponse(serviceName);
        LinkedHashMap<String, String> busiResMap = JSON.parseObject(message, new TypeReference<LinkedHashMap<String, String>>() {
        }, Feature.OrderedField);
        Set<Field> fields = Reflections.getFields(response.getClass());
        String key = null;
        for (Field field : fields) {
            Object convertedValue = null;
            key = field.getName();
            String newValue = busiResMap.get(key);
            if (ApiTypes.isSimpleType(field.getType())) {
                convertedValue = busiResMap.get(key);
            } else if (ApiTypes.isCollection(field)) {
                ParserConfig.getGlobalInstance().putDeserializer(Money.class, MoneyDeserializer.instance);
                Type gt = field.getGenericType();
                if (gt instanceof ParameterizedType) {
                    Type genericType = ((ParameterizedType) gt).getActualTypeArguments()[0];
                    if (genericType instanceof Class) {
                        convertedValue = jsonMarshallor.parseArray(newValue, (Class) genericType);
                    } else {
                        convertedValue = jsonMarshallor.parseArray(newValue, Reflections.getClassGenricType(response.getClass()));
                    }
                }
            }
            Reflections.invokeSetter(response, field.getName(), convertedValue);
        }
        return response;
    }

    /**
     * 验证签名
     *
     * @param response
     * @param respMessage
     */
    private void checkVerySign(FbankResponse response, Map<String, String> respMessage) {
        FbankApiMsgInfo fbankApiMsgInfo = response.getClass().getAnnotation(FbankApiMsgInfo.class);
        boolean isVerySign = fbankApiMsgInfo.isVerifySign();
        String signature = response.getSignature();
        if (isVerySign && Strings.equals(response.getCode(), FbankRespCodeEnum.RESP_CODE_10000.getCode()) && Strings.isNotBlank(signature)) {
            String partnerId = respMessage.get(FbankConstants.PARTNER_KEY);
            String message = respMessage.get(FbankConstants.RESP_MESSAGE_KEY);
            SortedMap<String, String> respMap = jsonMarshallor.parse(message, SortedMap.class);
            doVerifySign(respMap, signature, partnerId);
        }
    }

    public void setMessageFactory(MessageFactory messageFactory) {
        this.messageFactory = messageFactory;
    }
}
